﻿using AutoMapper;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using ProjectManageSystemApi.DbEntity;
using ProjectManageSystemApi.Enums;
using ProjectManageSystemApi.Models;
using ProjectManageSystemApi.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;

namespace ProjectManageSystemApi.Services.Impl
{
    public class UserServices : IUserServices
    {
        readonly IFreeSql _fsql;
        readonly Config _config;
        readonly UniqueKeyTool _uniqueKey;
        readonly IMapper _mapper;
        readonly IUserPasswordServices _userPassword;
        readonly IRoleMenuServices _role;
        public UserServices(IFreeSql fsql, Config config, UniqueKeyTool uniqueKey, IMapper mapper, IUserPasswordServices userPassword, IRoleMenuServices role)
        {
            _fsql = fsql;
            _config = config;
            _uniqueKey = uniqueKey;
            _mapper = mapper;
            _userPassword = userPassword;
            _role = role;
        }

        /// <summary>
        /// 登录返回token
        /// </summary>
        /// <param name="account"></param>
        /// <returns></returns>
        public TokenResponse GetToken(string account)
        {
            var result = new TokenResponse();
            var data = _fsql.Select<UserInfo>().Where(u => _fsql.Select<UserPasswordInfo>().Any(p => u.Id == p.Id && p.UserName == account)).ToOne();
            if (data == null)
            {
                return result;
            }
            DateTime exp = DateTime.Now.AddSeconds(_config.Expires + _config.ExtraExpires);
            var claims = new[]
            {
                new Claim(ClaimTypes.PrimarySid, data.Id.ToString()),
                new Claim(ClaimTypes.Name, account),
                new Claim(ClaimTypes.Email, data.Email ?? ""),
                new Claim(ClaimTypes.MobilePhone, data.PhoneNumber ?? ""),
                new Claim(JwtRegisteredClaimNames.Exp, $"{new DateTimeOffset(exp).ToUnixTimeSeconds()}"),
                new Claim(JwtRegisteredClaimNames.Nbf, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}")
            };
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config.SecurityKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var securityToken = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(
                issuer: _config.Issuer,
                audience: _config.Audience,
                claims: claims,
                expires: exp,
                signingCredentials: creds);
            var token = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler().WriteToken(securityToken);
            return new TokenResponse { Token = token, ExpiresIn = _config.Expires };
        }

        public TokenResponse RefreshToken(string oldToken)
        {
            var tokens = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler().ReadJwtToken(oldToken);
            string userName = tokens.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Name).Value;
            return GetToken(userName);
        }

        /// <summary>
        /// 根据 Tokne 获取 UserId
        /// </summary>
        /// <param name="token"></param>
        /// <param name="userId"></param>
        /// <returns></returns>
        public bool GetUserIdByToken(string token, out long userId)
        {
            if (token.IsNullOrEmpty()) 
            {
                userId = 0;
                return false;
            }
            try
            {
                var securityToken = new System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler().ReadJwtToken(token);
                var sid = securityToken.Claims.FirstOrDefault(x => x.Type == ClaimTypes.PrimarySid)?.Value;
                return long.TryParse(sid, out userId);
            }
            catch (Exception)
            {
                userId = 0;
                return false;
            }
        }

        /// <summary>
        /// 通过用户id获取用户信息
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public UserInfoDto GetUserByUserId(long userId)
        {
            var data = _fsql.Select<UserInfo>().Where(x => x.Id == userId).ToOne();
            return _mapper.Map<UserInfoDto>(data);
        }

        /// <summary>
        /// 判断是否存在该用户名
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public bool EnableByUserName(string userName)
        {
            return _fsql.Select<UserInfo>().Any(x => x.UserName == userName && x.Status == (int)UserInfoStatus.Exist)
                && _fsql.Select<UserPasswordInfo>().Any(x => x.UserName == userName);
        }

        /// <summary>
        /// 判断密码是否相同
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public bool PasswordSame(string userName, string password)
        {
            var data = _fsql.Select<UserPasswordInfo>().Where(x => x.UserName == userName).ToOne();
            string pwdMd5 = ApiUtilities.MD5Encrypt(password + _config.PwdSuffix);
            return data != null && string.Equals(data.Password, pwdMd5, StringComparison.OrdinalIgnoreCase);
        }

        public IEnumerable<UserRoleDto> GetAllUser()
        {
            var datas = _fsql.Select<UserInfo>()
                .IncludeMany(x => x.UserRoleLink)
                .IncludeMany(x => x.UserRoleLink, then =>
                 {
                     then.Include(y => y.Role);
                 });
            return _mapper.Map<List<UserRoleDto>>(datas.ToList());
        }

        /// <summary>
        /// 增加用户
        /// </summary>
        /// <param name="userInfo"></param>
        /// <returns></returns>
        public bool Insert(UserInfoDto userInfo)
        {
            try
            {
                _fsql.Transaction(() =>
                {
                    var data = _mapper.Map<UserInfo>(userInfo);
                    data.RegisterDate = DateTime.Now;
                    data.Status = (int)UserInfoStatus.Exist;

                    long id = _fsql.Insert(data).ExecuteIdentity();
                    if (id <= 0)
                    {
                        throw new DbTranException($"增加 user_info 表失败。");
                    }
                    var r = _role.BindUserRole(id, userInfo.RoleIds);
                    if (!r)
                    {
                        throw new DbTranException($"增加 sys_core_user_role_link 表失败。");
                    }
                    r = _userPassword.Create(id, userInfo.UserName, userInfo.Password);
                    if (!r)
                    {
                        throw new DbTranException($"增加 user_password_info 表失败。");
                    }
                });
                return true;
            }
            catch (DbTranException)
            {
                return false;
            }
        }

        /// <summary>
        /// 修改用户
        /// </summary>
        /// <param name="userInfo"></param>
        /// <returns></returns>
        public bool Update(UserInfoDto userInfo)
        {
            var data = _mapper.Map<UserInfo>(userInfo);
            return _fsql.Update<UserInfo>()
                .SetSource(data)
                .IgnoreColumns(u => new { u.UserName, u.RegisterDate, u.Status })
                .ExecuteAffrows() > 0;
        }

        /// <summary>
        /// 获取所有用户
        /// </summary>
        /// <param name="pageBo"></param>
        /// <returns></returns>
        public PageVo<IEnumerable<UserInfoDto>> GetAll(PageBo<UserParam> pageBo)
        {
            var datas = _fsql.Select<UserInfo>()
                .IncludeMany(x => x.UserRoleLink)
                .IncludeMany(x => x.UserRoleLink, then =>
                {
                    then.Include(y => y.Role);
                });
            if (!string.IsNullOrWhiteSpace(pageBo.Param.UserName))
            {
                datas = datas.Where(d => d.UserName == pageBo.Param.UserName);
            }
            if (!string.IsNullOrWhiteSpace(pageBo.Param.NickName))
            {
                datas = datas.Where(d => d.NickName == pageBo.Param.NickName);
            }
            if (pageBo.Param.RoleId > 0)
            {
                datas = datas.Where(d => _fsql.Select<SysCoreUserRoleLink>().Any(x => x.UserId == d.Id && x.RoleId == pageBo.Param.RoleId));
            }
            var total = datas.Count();
            datas = datas.Page(pageBo.Page, pageBo.Size);

            var data = new PageVo<IEnumerable<UserInfoDto>>
            {
                Page = pageBo.Page,
                Size = pageBo.Size,
                Datas = _mapper.Map<List<UserInfoDto>>(datas.ToList()),
                Total = total
            };
            return data;
        }

        /// <summary>
        /// 判断是否存在用户名
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public bool ExistByUserName(string userName)
        {
            return _fsql.Select<UserInfo>().Any(x => x.UserName == userName && x.Status < (int)UserInfoStatus.Delete)
                || _fsql.Select<UserPasswordInfo>().Any(x => x.UserName == userName);
        }

        /// <summary>
        /// 判断UserId是否存在
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool ExistByUserId(long id)
        {
            return _fsql.Select<UserInfo>().Any(u => u.Id == id && u.Status < (int)UserInfoStatus.Delete)
                && _fsql.Select<UserPasswordInfo>().Any(u => u.Id == id);
        }

        public bool UpdateUserStatus(long id, UserInfoStatus status)
        {
            return _fsql.Update<UserInfo>(id).Set(x => x.Status, (sbyte)status).ExecuteAffrows() > 0;
        }

        public bool AllowedLoginType(string userName, LoginType type) 
        {
            return _fsql.Select<UserInfo>()
                .Where(x=>x.UserName == userName)
                .Where(x => _fsql.Select<SysCoreUserRoleLink>().Where(l => l.UserId == x.Id)
                    .Where(l => _fsql.Select<SysCoreRoleExtra>().Where(e => e.RoleId == l.RoleId)
                        .WhereIf(type == LoginType.Pc, e => e.LoginPc)
                        .WhereIf(type == LoginType.Phone, e => e.LoginPhone)
                        .WhereIf(type == LoginType.None, e => false)
                        .Any())
                    .Any())
                .Any();
        }

    }
}
